﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace NeuralLib.Learning
{
    public class KohonenLearning
    {
        protected double _efficientWidth;

        protected double _efficientWidthScaleSpeed;

        protected double _initialEfficientWidth;

        protected double _initialLearningRate;

        protected int _iterationsCount;

        protected double _learningRate;

        protected double _learningRateScaleSpeed;

        protected KohonenNetwork _network;

        protected int[] _winnersIndexes;

        protected double[] _winningRates;       


        public virtual void Run(double[][] input){}

        protected double CalculateError(double[][] input)
        {
            double error = 0;
            var samplesCount = input.Count();
            var weightsCount = input[0].Length;
            for (var j = 0; j < samplesCount; j++)
            {
                var winnerInd = _network.KmLayer.Winner(input[j]);
                var winner = _network.KmLayer.Neurons[winnerInd];
                for (var i = 0; i < weightsCount; i++)
                {
                    var diff = input[j][i] - winner.Weights[i];
                    error += diff*diff;
                }
            }
            return error;
        }

        protected int[] DeadNeurons(double[][] input)
        {
            var dead = new List<int>();
            var neuronsCount = _network.KmLayer.Neurons.Count();
            var rates = new double[neuronsCount];
            var samplesCount = input.Count();

            for (var j = 0; j < samplesCount; j++)
            {
                var winnerInd = _network.KmLayer.Winner(input[j]);
                rates[winnerInd]++;
            }

            for (var i = 0; i < neuronsCount; i++)
                if (Math.Abs(rates[i]) < 0.0001)
                dead.Add(i);
            return dead.ToArray();
        }

        protected double NeuronsDistance(KohonenNeuron n1, KohonenNeuron n2)
        {
            var deltaX = n1.Coords.X - n2.Coords.X;
            var deltaY = n1.Coords.Y - n2.Coords.Y;
            var distanceSqr = deltaX * deltaX + deltaY * deltaY;
            return Math.Exp(-distanceSqr / (2 * _efficientWidth * _efficientWidth));
        }

        protected double UpdateEfficientWidth(int t)
        {
            _efficientWidth = _initialEfficientWidth * Math.Exp(-t / _efficientWidthScaleSpeed);           
            return _efficientWidth;
        }

        protected double UpdateLearningRate(int t)
        {
            _learningRate = _initialLearningRate * Math.Exp(-t / _learningRateScaleSpeed);
            return _learningRate;
        }
    }
}
